import asyncio
from asyncio import open_connection
from .handle import RpcHandle, Conn
from .route import Route
from . import Service
from lib.log import Log
from typing import Optional, Any


class RpcClient(Service):
    """
    rpc客户端
    """

    def __init__(self, ip, port, tag=None, from_address=None, route=None, *args, **kwargs):
        self.tag = tag  # 作为谁的manage(worker？gateway？timer？)也是client的别名
        self.from_address = from_address  # 使用client的服务地址
        self.working = False  # 是否工作中
        self.route = route if route else Route()  # rpc路由
        self.rpc_handle: Optional[RpcHandle] = None  # 消息处理
        self.ip = ip
        self.port = port
        self.address = f"{ip}:{port}"
        self._name = f"{tag}_{self.address}"
        self.conn = None
        self.log = Log(f"{tag}_{ip}_{port}")

    async def connect(self) -> Optional[str]:
        """链接服务器"""
        # print(f"RpcClient[{self.tag}] 开始链接至{self.address}")
        if self.working:
            return

        try:
            self.conn = Conn(*await open_connection(self.ip, self.port), name=self.name)
            self.working = True
        except ConnectionRefusedError:
            pass
        except Exception as e:
            print(f"RpcClient connect error=>{e}")
            pass

        if not self.working:
            print(f"RpcClient[{self.tag}] {self.address} connect fail!")
            return "fail"

        # print(f"RpcClient[{self.tag}] {self.address} connect success!")

        self.rpc_handle = RpcHandle(self, self.conn, self.route, self.close)
        self.rpc_handle.set_log(self.log)

        # 开始消息处理
        asyncio.create_task(self.rpc_handle.run(), name=self.name)

        return await self.register(self.from_address, tag=self.tag)

    async def close(self, _):
        """关闭"""
        self.working = False

    async def register(self, *args, **kwargs) -> str:
        """注册"""
        _res = await self.call("register", *args, **kwargs)
        print(f"RpcClient[{self.tag}] {self.from_address} {_res}")
        return _res

    @property
    def name(self) -> str:
        return self._name

    def notice(self, func_name, *args, **kwargs) -> None:
        """通知"""
        self.rpc_handle.notice(func_name, *args, **kwargs)

    def callback(self, func_name, func_args, callback_func, *args, **kwargs) -> None:
        """回调"""
        self.rpc_handle.callback(func_name, func_args, callback_func, *args, **kwargs)

    async def call(self, func_name, *args, **kwargs) -> Any:
        """等待"""
        return await self.rpc_handle.call(func_name, *args, **kwargs)


if __name__ == '__main__':
    async def start():
        client = RpcClient("10.0.0.153", 9001, tag="test", from_address="test")
        await asyncio.gather(
            client.connect(),
            asyncio.Future()
        )


    try:
        asyncio.run(start())
    except KeyboardInterrupt:
        pass
